﻿using CLCommuModbus.Config;
using CLVisionModbus;
using NationalInstruments.TestStand.Interop.API;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

namespace CLCommuModbus
{
    public partial class CommuCaller
    {
        static Stopwatch stopwatch = new Stopwatch();

        internal void ReadObjectValueFromPLC_handShakeMode2(int objectID, PropertyObject containerToReceiveValue, out int ErrorCode, out string ErrorText)
        {
            ErrorCode = 0;
            ErrorText = "NoError";
            try
            {
                // ******读标志位（数据区域最后一位），为0时继续，为1异常
                var flagValue = (ushort)ReadFromPLC((int)ObjectHandShakeMode2.FlagAddress, DataType.USHORT, ModbusDataType.HoldingRegisters, out ErrorCode, out ErrorText);
                if (flagValue != 0)
                {
                    ErrorCode = -1;
                    ErrorText = $"Slave读取异常：标志位{(int)ObjectHandShakeMode2.FlagAddress}未清空！";
                    return;
                }
                //*****读取plc的objectID，判断plc是否清空上次数据
                var objectValue = (ushort)ReadFromPLC((int)ObjectHandShakeMode2.ObjectIDAddress, DataType.USHORT, ModbusDataType.HoldingRegisters, out ErrorCode, out ErrorText);
                if (objectValue != 0)
                {
                    ErrorCode = -1;
                    ErrorText = $"Slave读取异常：ObjectID{(int)ObjectHandShakeMode2.ObjectIDAddress}未清空！";
                    return;
                }

                //*****写入要读取的ObjectID
                WriteToPLC((int)ObjectHandShakeMode2.ObjectIDAddress, DataType.USHORT, objectID, ModbusDataType.HoldingRegisters, out ErrorCode, out ErrorText);
                if (ErrorCode == -1)
                {
                    ErrorText = "向PLC写入ObjectID失败！";
                }
                //*****轮询标志位   
                stopwatch.Restart();
                //超过3s不为1，返回false
                while (stopwatch.ElapsedMilliseconds < 3000)
                {
                    //PLC是否写完标志
                    var value = (ushort)ReadFromPLC((int)ObjectHandShakeMode2.FlagAddress, DataType.USHORT, ModbusDataType.HoldingRegisters, out ErrorCode, out ErrorText);
                    if ((ushort)value == 1)
                    {
                        //将数据写入TS
                        WriteObjectValueToTS_handShakeMode2(objectID, containerToReceiveValue, out ErrorCode, out ErrorText);
                        //*******读取完成    标志位置为0
                        WriteToPLC((int)ObjectHandShakeMode2.FlagAddress, DataType.USHORT, 0, ModbusDataType.HoldingRegisters, out ErrorCode, out ErrorText);
                        return;
                    }
                }
                stopwatch.Stop();
                ErrorCode = -1;
                ErrorText = "PLC写入数据超时！";
            }
            catch (Exception ex)
            {
                ErrorCode = -1;
                ErrorText = "HandShakeMode2模式下读取Object数据失败！" + ex.Message;
            }
        }
        internal void WriteObjectValueToPLC_handShakeMode2(int objectID, PropertyObject containerToSendValue, out int ErrorCode, out string ErrorText)
        {
            ErrorCode = 0;
            ErrorText = "NoError";
            try
            {
                //******读标志位（数据区域最后一位），为0时继续，为1异常
                var flagValue = (ushort)ReadFromPLC((int)ObjectHandShakeMode2.FlagAddress, DataType.USHORT, ModbusDataType.HoldingRegisters, out ErrorCode, out ErrorText);
                if (flagValue != 0)
                {
                    ErrorCode = -1;
                    ErrorText = $"Slave写入异常：标志位{(int)ObjectHandShakeMode2.FlagAddress}未清空！";
                    return;
                }
                var objectValue = (ushort)ReadFromPLC((int)ObjectHandShakeMode2.ObjectIDAddress, DataType.USHORT, ModbusDataType.HoldingRegisters, out ErrorCode, out ErrorText);

                if (objectValue != 0)
                {
                    ErrorCode = -1;
                    ErrorText = $"Slave写入异常：ObjectID{(int)ObjectHandShakeMode2.ObjectIDAddress}未清空！";
                    return;
                }

                //********写入数据
                ushort[] ushorts = ReadObjectValueFromTS_handShakeMode2(objectID, containerToSendValue, out ErrorCode, out ErrorText);
                if (ErrorCode == -1)
                {
                    ErrorText = "从TestStand中读取Object数据失败！";
                    return;
                }
                WriteUshortsToPLC((int)ObjectHandShakeMode2.StructStartAddress, ushorts, ModbusDataType.HoldingRegisters, out ErrorCode, out ErrorText);
                if (ErrorCode == -1)
                {
                    ErrorText = "向PLC写入Object数据失败";
                    return;
                }

                //******轮询标志位，PLC是否读取完成
                stopwatch.Restart();
                while (stopwatch.ElapsedMilliseconds < 3000)
                {
                    var value = ReadFromPLC((int)ObjectHandShakeMode2.FlagAddress, DataType.USHORT, ModbusDataType.HoldingRegisters, out ErrorCode, out ErrorText);
                    if (ErrorCode == -1)
                    {
                        ErrorText = "读取标识位失败！";
                        return;
                    }
                    //******值为0  PLC读取完成
                    if ((ushort)value == 0)
                    {
                        return;
                    }
                }
                stopwatch.Stop();
                ErrorCode = -1;
                ErrorText = "PLC读取数据超时！";
            }
            catch (Exception ex)
            {
                ErrorCode = -1;
                ErrorText = "HandShakeMode2模式下写入Object数据失败！" + ex.Message;
            }
        }

        internal void WriteObjectValueToTS_handShakeMode2(int objectID, PropertyObject containerToReceiveValue, out int ErrorCode, out string ErrorText)
        {
            ErrorCode = 0;
            ErrorText = "NoError";
            try
            {
                List<ObjectItemConfig> configurationItems = objectConfigOption.objectConfigDict[objectID].objectItemConfigs;
                List<string> objectNames = configurationItems.Select(x => x.Name).ToList();

                //获取Object下的所有子节点
                PropertyObject[] propertyObjects = containerToReceiveValue.GetSubProperties("", 0);
                List<string> propertyObjectNames = propertyObjects.Select(x => x.Name).ToList();

                //判断元素是否相同
                //objectNames中有而propertyObjectNames中没有的元素集合difference1
                IEnumerable<string> difference1 = objectNames.Except(propertyObjectNames);
                //propertyObjectNames中有而objectNames中没有的元素集合difference2
                IEnumerable<string> difference2 = propertyObjectNames.Except(objectNames);
                //object参数数量和名称一致
                if (difference1.Count() == 0 && difference2.Count() == 0)
                {
                    ushort[] valueArray = ReadUshortsFromPLC((int)ObjectHandShakeMode2.StructStartAddress, (int)ObjectHandShakeMode2.TotalDataLength, ModbusDataType.HoldingRegisters, out ErrorCode, out ErrorText) as ushort[];
                    if (ErrorCode == -1)
                    {
                        return;
                    }
                    int DataIndexInRange = 0;
                    object value;
                    //遍历object
                    for (int j = 0; j < configurationItems.Count; j++)
                    {
                        //遍历TS节点
                        for (int i = 0; i < propertyObjects.Length; i++)
                        {
                            //判断名称是否一致
                            string disName = propertyObjects[i].Type.DisplayString.ToLower();
                            string propertyName = propertyObjects[i].Name;

                            ModbusDataType modbusDataType = configurationItems[j].ModbusDataType;
                            string objectItemName = configurationItems[j].Name;
                            DataType dataType = configurationItems[j].DataType;
                            if (propertyName == objectItemName)
                            {
                                if (disName.Contains("array"))
                                {
                                    List<int> modbusAddress = configurationItems[j].Address;
                                    for (int k = 0; k < modbusAddress.Count; k++)
                                    {
                                        switch (dataType)
                                        {
                                            case DataType.USHORT:
                                                value = valueArray[DataIndexInRange];
                                                propertyObjects[i].SetValNumberByOffset(k, 0, Convert.ToDouble(value));
                                                DataIndexInRange += (int)ObjectHandShakeMode2.ObjectUshortDataLength;
                                                break;
                                            case DataType.INT:
                                                /////handshakemode2 握手信号下 int类型占32位,占2个地址长度
                                                value = ConvertData.UshortsToInt(valueArray.ToList(), DataIndexInRange);
                                                propertyObjects[i].SetValNumberByOffset(k, 0, Convert.ToDouble(value));
                                                DataIndexInRange += (int)ObjectHandShakeMode2.ObjectIntDataLength;
                                                break;
                                            case DataType.FLOAT:
                                                /////handshakemode2 握手信号下 float类型占32位,占2个地址长度
                                                value = ConvertData.UshortsToFloat(valueArray.ToList(), DataIndexInRange);
                                                propertyObjects[i].SetValNumberByOffset(k, 0, Convert.ToDouble(value));
                                                DataIndexInRange += (int)ObjectHandShakeMode2.ObjectFloatDataLength;
                                                break;
                                            case DataType.DOUBLE:
                                                /////handshakemode2 握手信号下 double类型占64位,占4个地址长度
                                                value = ConvertData.UshortsToDouble(valueArray.ToList(), DataIndexInRange);
                                                propertyObjects[i].SetValNumberByOffset(k, 0, Convert.ToDouble(value));
                                                DataIndexInRange += (int)ObjectHandShakeMode2.ObjectDoubleDataLength;
                                                break;
                                            case DataType.STRING:
                                                /////handshakemode2 握手信号下 string类型占40个地址长度
                                                value = ConvertData.UshortsToString(valueArray.ToList(), DataIndexInRange, (int)ObjectHandShakeMode2.ObjectStringDataLength);
                                                propertyObjects[i].SetValStringByOffset(k, 0, value.ToString());
                                                DataIndexInRange += (int)ObjectHandShakeMode2.ObjectStringDataLength;
                                                break;
                                            default:
                                                break;
                                        }
                                    }
                                }
                                else
                                {
                                    switch (dataType)
                                    {
                                        case DataType.USHORT:
                                            value = valueArray[DataIndexInRange];
                                            propertyObjects[j].SetValNumber("", 0, Convert.ToDouble(value));
                                            DataIndexInRange += (int)ObjectHandShakeMode2.ObjectUshortDataLength;
                                            break;
                                        case DataType.INT:
                                            value = ConvertData.UshortsToInt(valueArray.ToList(), DataIndexInRange);
                                            propertyObjects[j].SetValNumber("", 0, Convert.ToDouble(value));
                                            DataIndexInRange += (int)ObjectHandShakeMode2.ObjectIntDataLength;
                                            break;
                                        case DataType.FLOAT:
                                            value = ConvertData.UshortsToFloat(valueArray.ToList(), DataIndexInRange);
                                            propertyObjects[j].SetValNumber("", 0, Convert.ToDouble(value));
                                            DataIndexInRange += (int)ObjectHandShakeMode2.ObjectFloatDataLength;
                                            break;
                                        case DataType.DOUBLE:
                                            /////handshakemode2 握手信号下 double类型占64位,占4个地址长度
                                            value = ConvertData.UshortsToDouble(valueArray.ToList(), DataIndexInRange);
                                            propertyObjects[j].SetValNumber("", 0, Convert.ToDouble(value));
                                            DataIndexInRange += (int)ObjectHandShakeMode2.ObjectDoubleDataLength;
                                            break;
                                        case DataType.STRING:
                                            value = ConvertData.UshortsToString(valueArray.ToList(), DataIndexInRange, (int)ObjectHandShakeMode2.ObjectStringDataLength);
                                            propertyObjects[j].SetValString("", 0, value.ToString());
                                            DataIndexInRange += (int)ObjectHandShakeMode2.ObjectStringDataLength;
                                            break;
                                        default:
                                            break;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ErrorCode = -1;
                ErrorText = "将Object数据写入TestStand失败！" + ex.Message;
            }
        }



        internal ushort[] ReadObjectValueFromTS_handShakeMode2(int objectID, PropertyObject containerToReceiveValue, out int ErrorCode, out string ErrorText)
        {
            ErrorCode = 0;
            ErrorText = "NoError";
            List<ushort> list = null;
            try
            {
                List<ObjectItemConfig> configurationItems = objectConfigOption.objectConfigDict[objectID].objectItemConfigs;
                List<string> objectNames = configurationItems.Select(x => x.Name).ToList();

                //获取Object下的所有子节点
                PropertyObject[] propertyObjects = containerToReceiveValue.GetSubProperties("", 0);
                List<string> propertyObjectNames = propertyObjects.Select(x => x.Name).ToList();

                //判断元素是否相同
                //objectNames中有而propertyObjectNames中没有的元素集合difference1
                IEnumerable<string> difference1 = objectNames.Except(propertyObjectNames);
                //propertyObjectNames中有而objectNames中没有的元素集合difference2
                IEnumerable<string> difference2 = propertyObjectNames.Except(objectNames);
                if (difference1.Count() == 0 && difference2.Count() == 0)
                {
                    list = new List<ushort>();
                    //object参数数量和名称一致
                    for (int j = 0; j < configurationItems.Count; j++)
                    {
                        //遍历TS节点
                        for (int i = 0; i < propertyObjects.Length; i++)
                        {
                            string disName = propertyObjects[i].Type.DisplayString.ToLower();

                            string propertyName = propertyObjects[i].Name;

                            ModbusDataType modbusDataType = configurationItems[j].ModbusDataType;
                            string objectItemName = configurationItems[j].Name;
                            DataType dataType = configurationItems[j].DataType;
                            List<int> address = configurationItems[j].Address;
                            if (propertyName == objectItemName)
                            {
                                for (int k = 0; k < address.Count; k++)
                                {
                                    object result;
                                    if (disName.Contains("number"))
                                    {
                                        switch (dataType)
                                        {
                                            case DataType.USHORT:
                                                result = disName.Contains("array") ? propertyObjects[i].GetValNumberByOffset(k, 0) : propertyObjects[i].GetValNumber("", 0);
                                                list.Add(Convert.ToUInt16(result));
                                                break;
                                            case DataType.INT:
                                                result = disName.Contains("array") ? propertyObjects[i].GetValNumberByOffset(k, 0) : propertyObjects[i].GetValNumber("", 0);
                                                list.AddRange(ConvertData.IntToUshorts(Convert.ToInt32(result)));
                                                break;
                                            case DataType.FLOAT:
                                                result = disName.Contains("array") ? propertyObjects[i].GetValNumberByOffset(k, 0) : propertyObjects[i].GetValNumber("", 0);
                                                list.AddRange(ConvertData.FloatToUshorts(Convert.ToSingle(result)));
                                                break;
                                            case DataType.DOUBLE:
                                                result = disName.Contains("array") ? propertyObjects[i].GetValNumberByOffset(k, 0) : propertyObjects[i].GetValNumber("", 0);
                                                list.AddRange(ConvertData.DoubleToUshorts(Convert.ToDouble(result)));
                                                break;
                                            case DataType.STRING:
                                                result = disName.Contains("array") ? propertyObjects[i].GetValNumberByOffset(k, 0) : propertyObjects[i].GetValNumber("", 0);
                                                list.AddRange(ConvertData.StringToUshorts(result.ToString()));
                                                break;
                                            default:
                                                ErrorCode = -1;
                                                ErrorText = $"本地参数的DataType参数{dataType}有误或不支持！";
                                                break;
                                        }
                                    }
                                    else if (disName.Contains("string"))
                                    {
                                        result = disName.Contains("array") ? propertyObjects[i].GetValStringByOffset(k, 0) : propertyObjects[i].GetValString("", 0);
                                        list.AddRange(ConvertData.StringToUshorts(result.ToString()));
                                    }
                                }
                            }

                        }
                    }
                    //Data区域内  若无数据 集合填充为0
                    for (int i = list.Count; i < (int)ObjectHandShakeMode2.ObjectDataLength; i++)
                    {
                        list.Add(0);
                    }
                    //标志位信号
                    list.Add((ushort)ObjectHandShakeMode2.ObjectWriteFinishFlag);

                    //ObjectID
                    list.Add(Convert.ToUInt16(objectID));
                    return list.ToArray();
                }
                else
                {
                    ErrorCode = -1;
                    ErrorText = "TS的objectItem数量/名称与配置参数不一致";
                    return null;
                }
            }
            catch (Exception ex)
            {
                ErrorCode = -1;
                ErrorText = "从TestStand读取Object数据失败！" + ex.Message ;
                return null;
            }
        }



    }
}

